package common;

import java.io.PrintStream;
import java.util.ArrayList;

import astnode.ASTNode;
import astnode.AssignStmtNode;
import astnode.BinaryOperationNode;
import astnode.BranchStmtNode;
import astnode.CallNode;
import astnode.CaseNode;
import astnode.CompoundStmtNode;
import astnode.DeclarationNode;
import astnode.ExpressionNode;
import astnode.FunctionDeclarationNode;
import astnode.IfStmtNode;
import astnode.LiteralNode;
import astnode.LoopStmtNode;
import astnode.MarkerStmtNode;
import astnode.ParameterNode;
import astnode.ReturnStmtNode;
import astnode.StmtNode;
import astnode.UnitaryOperationNode;
import astnode.VariableDeclarationNode;
import astnode.VariableNode;

public class PrintVisitor implements Visitor {

	private int level =0;
	private PrintStream writer = null;
	private ArrayList<String> spellingTable = null;
	private Visitor nextLevelVisitor(){
		return new PrintVisitor( level+1 , writer, spellingTable);
	}

	private void println(String info, int lineNumber){
		printIndent();
		writer.print( lineNumber+":");
		writer.println(info);
	}

	private void printlnfo(String info){
		printIndent();
		writer.println("     "+info);
	}

	/**
	 * Initial print visitor
	 * @param level
	 * @param writer
	 * @param spellingTable
	 */
	public PrintVisitor( int level , PrintStream writer, ArrayList<String> spellingTable){
		this.level = level;
		this.writer = writer;
		this.spellingTable = spellingTable;
	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visit(astnode.ASTNode)
	 */
	@Override
	public void visit( ASTNode node){
		if( writer==null ){
			return;
		}
		if( node instanceof VariableDeclarationNode ){
			visitVariableDeclarationNode( (VariableDeclarationNode) node );
		}else if( node instanceof FunctionDeclarationNode ){
			visitFunctionDeclarationNode( (FunctionDeclarationNode) node );
		}else if( node instanceof LiteralNode ){
			visitLiteralNode( (LiteralNode) node );
		}else if( node instanceof BinaryOperationNode ){
			visitBinaryOperationNode( (BinaryOperationNode) node );
		}else if( node instanceof UnitaryOperationNode ){
			visitUnitaryOperationNode( (UnitaryOperationNode) node );
		}else if( node instanceof VariableNode ){
			visitVariableNode( (VariableNode) node );
		}else if( node instanceof CallNode ){
			visitCallNode( (CallNode) node );
		}else if( node instanceof BranchStmtNode ){
			visitBranchStmtNode( (BranchStmtNode) node );
		}else if( node instanceof CompoundStmtNode ){
			visitComspoundStmtNode( (CompoundStmtNode) node );
		}else if( node instanceof MarkerStmtNode ){
			visitMarkerStmtNode( (MarkerStmtNode) node );
		}else if( node instanceof ReturnStmtNode ){
			visitReturnStmtNode( (ReturnStmtNode) node );
		}else if( node instanceof IfStmtNode ){
			visitIfStmtNode( (IfStmtNode) node );
		}else if( node instanceof LoopStmtNode ){
			visitLoopStmtNode( (LoopStmtNode) node );
		}else if( node instanceof CaseNode ){
			visitCaseNode( (CaseNode) node );
		}else if( node instanceof ParameterNode ){
			visitParameterNode( (ParameterNode) node );
		}else if( node instanceof AssignStmtNode ){
			visitAssignStmtNode( (AssignStmtNode) node );
		}
	}


	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitAssignStmtNode(astnode.AssignStmtNode)
	 */
	@Override
	public void visitAssignStmtNode(AssignStmtNode node) {
		println("--AssignStmtNode",node.getLineNum());
		printlnfo("id:"+node.getIdentifier()+"=>"+ spellingTable.get( node.getIdentifier() ) );		
		if( node.getIndex()!=null ){
			printlnfo("index:");
			node.getIndex().accept( nextLevelVisitor() );
		}else{
			printlnfo("index: null");
		}
		
		if( node.getRightvalue()!=null ){		
			printlnfo("rightValue:");
			node.getRightvalue().accept( nextLevelVisitor() );
		}else{
			printlnfo("rightValue: null");
		}
	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitParameterNode(astnode.ParameterNode)
	 */
	@Override
	public void visitParameterNode(ParameterNode node) {
		println("--ParameterNode", node.getLineNum());
		printlnfo("type:"+node.getType());
		printlnfo("id:"+node.getIdentifier()+"=>"+ spellingTable.get( node.getIdentifier() ) );
		printlnfo("isRef:"+node.isRef());
		printlnfo("isArray:"+node.isArray() );
	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitCaseNode(astnode.CaseNode)
	 */
	@Override
	public void visitCaseNode(CaseNode node) {
		println("--Case", node.getLineNum()); 
	    printlnfo("number:"+node.getNumber());		
		printlnfo("stmt:");
		node.getStmt().accept( nextLevelVisitor() );
	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitLoopStmtNode(astnode.LoopStmtNode)
	 */
	@Override
	public void visitLoopStmtNode(LoopStmtNode node) {
		println("--Loop ", node.getLineNum());

		ArrayList<StmtNode> statements = node.getStatements();
		if( statements.size()>0 ){
			printlnfo("statements:");
			for( int i=0; i<statements.size() ; i++ ){
				statements.get(i).accept( nextLevelVisitor() );
			}
		}else{
			printlnfo("statements: null");
		}

	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitIfStmtNode(astnode.IfStmtNode)
	 */
	@Override
	public void visitIfStmtNode(IfStmtNode node) {
		println("--IfStmt ", node.getLineNum() );
		printlnfo("condition:");
		node.getCondition().accept( nextLevelVisitor() );
		printlnfo("true body:");
		node.getTrueBody().accept( nextLevelVisitor() );	
		
		if( node.getElseBody()!=null ){
			printlnfo("else body:");
			node.getElseBody().accept( nextLevelVisitor() );
		}else{
			printlnfo("else body: null" );
		}
	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitReturnStmtNode(astnode.ReturnStmtNode)
	 */
	@Override
	public void visitReturnStmtNode(ReturnStmtNode node) {
		println("--ReturnStmt ", node.getLineNum());
		if( node.getValue()!=null ){
			printlnfo("value:");
			node.getValue().accept( nextLevelVisitor() );
		}else{
			printlnfo("value: null");
		}
	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitMarkerStmtNode(astnode.MarkerStmtNode)
	 */
	@Override
	public void visitMarkerStmtNode(MarkerStmtNode node) {
		println("--MakerStmt", node.getLineNum());
		printlnfo(	"type:"+ node.getType().toString() );

	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitComspoundStmtNode(astnode.CompoundStmtNode)
	 */
	@Override
	public void visitComspoundStmtNode(CompoundStmtNode node) {
		println("--CompoundStmt", node.getLineNum() );
		ArrayList<DeclarationNode> declarations = node.getDeclarations();
		ArrayList<StmtNode> stmtNodes = node.getStmtNode();

		if( declarations.size()>0 ){
			printlnfo("declarations:");
			for( int i=0; i<declarations.size() ; i++ ){
				declarations.get(i).accept( nextLevelVisitor() );
			}
		}else{
			printlnfo("declarations: null");
		}
		
		if( stmtNodes.size()>0 ){
			printlnfo("stmtNodes");
			for( int i=0; i<stmtNodes.size() ; i++ ){
				//debug
				if( stmtNodes.get(i)!=null )
					stmtNodes.get(i).accept( nextLevelVisitor() );
			}
		}else{
			printlnfo("stmtNodes: null");
		}

	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitBranchStmtNode(astnode.BranchStmtNode)
	 */
	@Override
	public void visitBranchStmtNode(BranchStmtNode node) {
		println("--BranchStmt", node.getLineNum());
		node.getExp().accept( nextLevelVisitor() );

		ArrayList<CaseNode> cases = node.getCases();
		printlnfo("cases:");
		for( int i=0; i<cases.size() ; i++ ){
			cases.get(i).accept( nextLevelVisitor() );
		}
	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitCallNode(astnode.CallNode)
	 */
	@Override
	public void visitCallNode(CallNode node) {
		println("--CallNode", node.getLineNum());
		printlnfo("type:"+node.getType());
		printlnfo( "id:"+node.getIdentifier() +"=>"+ spellingTable.get( node.getIdentifier() ));
		if( node.getDeclaration()!=null ){
			printlnfo("declaration line number:"+node.getDeclaration().getLineNum() );
		} else{
			printlnfo("declaration: null");
		}
		
		
		ArrayList<ExpressionNode> parameters = node.getArguements();
		if( parameters.size()>0 ){
			printlnfo("parameters:");
			for( int i=0; i<parameters.size() ; i++ ){
				parameters.get(i).accept( nextLevelVisitor() );
			}
		}else{
			printlnfo("parameters: null");
		}


	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitVariableNode(astnode.VariableNode)
	 */
	@Override
	public void visitVariableNode(VariableNode node) {
		println("--VariableNode", node.getLineNum() );
		printlnfo("type:"+node.getType());
		printlnfo("id:"+node.getIdentifier() +"=>"+ spellingTable.get( node.getIdentifier() ));
		if( node.getDeclaration()!=null ){
			printlnfo("declaration line number:"+node.getDeclaration().getLineNum() );
		} else{
			printlnfo("declaration: null");
		}
		
		if( node.getIndex()!=null ){
			printlnfo("index:");
			node.getIndex().accept( nextLevelVisitor() );
		}else{
			printlnfo("index: null");
		}

	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitUnitaryOperationNode(astnode.UnitaryOperationNode)
	 */
	@Override
	public void visitUnitaryOperationNode(UnitaryOperationNode node) {
		println("--UnitaryOperationNode", node.getLineNum() );
		printlnfo( "type:"+node.getType() );
		printlnfo( "operator:"+node.getOperator() );
		if( node.getOperand()!=null ){
			printlnfo("oprand:");
			node.getOperand().accept( nextLevelVisitor() );
		}else{
			printlnfo("oprand: null");
		}

	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitBinaryOperationNode(astnode.BinaryOperationNode)
	 */
	@Override
	public void visitBinaryOperationNode(BinaryOperationNode node) {
		println("--BinaryOperationNode", node.getLineNum() );
		printlnfo( "type:"+node.getType() );
		printlnfo( "operator:"+node.getOperator());		
		
		if( node.getLeftExp()!=null ){
			printlnfo( "leftExp:");
			node.getLeftExp().accept( nextLevelVisitor() );
		}else{
			printlnfo( "leftExp: null");
		}	
	
		if( node.getRightExp()!=null ){
			printlnfo("rightExp:");
			node.getRightExp().accept( nextLevelVisitor() );
		}else{
			printlnfo( "rightExp: null");
		}

	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitLiteralNode(astnode.LiteralNode)
	 */
	@Override
	public void visitLiteralNode(LiteralNode node) {
		println("--LiteralNode", node.getLineNum() );
		printlnfo("type:"+node.getType() );
		printlnfo( "value:"+node.getValue());

	}

	public void printIndent(){
		for( int i = 0; i<level; i++ ){
			writer.print("\t\t");
		}
	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitVariableDeclarationNode(astnode.VariableDeclarationNode)
	 */
	@Override
	public void visitVariableDeclarationNode( VariableDeclarationNode node ){
		println( "--VariableDeclaration", node.getLineNum() );
		printlnfo("type:"+node.getType().toString());
		printlnfo("id:"+node.getIdentifier() +"=>"+ spellingTable.get( node.getIdentifier() ) );
		if( node.getSize()!=null ){
			printlnfo("size:");
			node.getSize().accept( nextLevelVisitor() );
		}else{
			printlnfo("size: null");
		}

		ArrayList<VariableDeclarationNode> list = node.getFollowList();
		if( list.size()>0 ){
			printlnfo("follow declaration:");
			for( int i=0; i<list.size(); i++ ){
				list.get(i).accept( nextLevelVisitor() );
			}
		}else{
			printlnfo("follow declaration: null");
		}

	}

	/* (non--Javadoc)
	 * @see commonmodule.Visitor#visitFunctionDeclarationNode(astnode.FunctionDeclarationNode)
	 */
	@Override
	public void visitFunctionDeclarationNode( FunctionDeclarationNode node ){
		println( "--FunctionDeclarationNode", node.getLineNum() );
		printlnfo( "type:"+node.getType().toString());
		printlnfo( "id:"+node.getIdentifier() +"=>"+ spellingTable.get( node.getIdentifier() ));

		ArrayList<ParameterNode> parameters =  node.getParameterList();
		if( parameters.size()>0 ){
			printlnfo("arguements:");
			for( int i=0; i<parameters.size(); i++ ){
				parameters.get(i).accept( nextLevelVisitor() );
			}
		}else{
			printlnfo("arguements: null");
		}
		
		if( node.getComspond()!=null ){
			printlnfo("comspound:");
			node.getComspond().accept( nextLevelVisitor() );
		}else{
			printlnfo("comspound: null");
		}


	}
}
